package com.apps.sworld.dao.impl;

import com.apps.sannotation.service.enums.CacheModel;
import com.apps.saspect.ScriptEvent;
import com.apps.saspect.enums.ThreadLockPattern;
import com.apps.sconfig.SDevFastConfig;
import com.apps.shelper.RedisCacheHelper;
import com.apps.sworld.sScript;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.apps.sannotation.identity.GmtCreate;
import com.apps.sannotation.tool.enums.SortType;
import com.apps.saspect.ThreadLock;
import com.apps.sexctption.DataException;
import com.apps.sexctption.DbException;
import com.apps.sexctption.RepeatExctption;
import com.apps.shelper.AnnotationHelper;
import com.apps.shelper.ClassHelper;
import com.apps.sworld.dao.sDao;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;

@Slf4j
@SuppressWarnings("unchecked")
public class sDaoImpl<M extends BaseMapper<T>, T> implements sDao<T> {

    @Autowired
    protected M baseMapper;

    @Autowired
    private SDevFastConfig sDevFastConfig;

    /**
     * 校验SQL执行是否成功
     * @param result
     * @return
     */
    protected boolean retBool(Integer result) {
        return SqlHelper.retBool(result);
    }

    /**
     * 获取tableid字段的名称
     * @return
     */
    protected String tableIdName()
    {
        return AnnotationHelper.getAnnotationFieldName(TableId.class,currentModelClass());
    }
    /**
     * 获取sqlSession
     * @return
     */
    protected SqlSession sqlSessionBatch() {
        return SqlHelper.sqlSessionBatch(currentModelClass());
    }

    /**
     * 释放sqlSession
     *
     * @param sqlSession session
     */
    protected void closeSqlSession(SqlSession sqlSession) {
        SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(currentModelClass()));
    }

    /**
     * 获取SqlStatement
     *
     * @param sqlMethod
     * @return
     */
    protected String sqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.table(currentModelClass()).getSqlStatement(sqlMethod.getMethod());
    }

    /**
     * 通用验证方法
     * @param t
     */
    @SneakyThrows
    protected void CommonVerifyModel(T t)
    {
        //验证模型的数据是否符合规则
        AnnotationHelper.verifyModelFiled(t);
        //校验不能重复的字段是否数据库已经有值
        validNoRepeat(t);
    }

    @SneakyThrows
    protected void CommonVerifyModel(List<T> t)
    {
        for(int i=0;i<t.size();i++) {
            //验证模型的数据是否符合规则
            AnnotationHelper.verifyModelFiled(t.get(i));
            //校验不能重复的字段是否数据库已经有值
            validNoRepeat(t.get(i));
        }
    }

    /**
     * 验证数据库是否有相同字段
     * @param t
     */
    protected void validNoRepeat(T t)
    {
        //校验不能重复的字段是否数据库已经有值
        Map<String, Object> maps = AnnotationHelper.getModelNoRepeatNames(t);
        if (maps.size() > 0) {
            QueryWrapper<T> queryWrapper = new QueryWrapper<>();
            int count=0;
            for (Map.Entry<String, Object> entry : maps.entrySet()) {
                if(count < maps.size()-1)
                    queryWrapper.eq(entry.getKey(), entry.getValue()).or();
                else
                    queryWrapper.eq(entry.getKey(), entry.getValue());
                count++;
            }

            //查询结果不为0说明有重复数据
            List<T> result=query(queryWrapper);
            //如果自增ID不为空则说明是在更新，则需要排除自身
            Object tableIdValue=ClassHelper.getFiledValue(t,tableIdName());
            if(null != tableIdValue)
            {
                for (int i=0;i<result.size();i++) {
                    if(ClassHelper.getFiledValue(result.get(i),tableIdName()).equals(tableIdValue)) {
                        result.remove(i);
                    }
                }
            }
            if (null !=  result && result.size() > 0)
                   throw new RepeatExctption("以上是存在关键字段值一样的数据,不能重复插入");

        }
    }

    /**
     * 获取当前myBits的模型
     * @return
     */

    @Override
    public Class<T> currentModelClass()  {
        return ReflectionKit.getSuperClassGenericType(getClass(), 1);
    }

    /**
     * 保存到数据库
     * @param t
     */
    @ScriptEvent(script = sScript.class,beForeMethodName = "onCreate(t)",afterMethodName = "onCreated(result)")
    @Transactional(rollbackFor = DbException.class)
    @ThreadLock(patten = ThreadLockPattern.OnSingleThread)
    @Override
    public T saveToDB(T t) {
        AnnotationHelper.handleModelFiled(t);
        if(sDevFastConfig.getVerifyLevel() >= 1) {
            CommonVerifyModel(t);
        }
        if(!retBool(baseMapper.insert(t)))
            throw new DataException("保存失败");
        //缓存更新
        RedisCacheHelper.add(t,this.getClass(),true);
        this.onCreate(t);
        this.onChange();
        return t;
    }

    /**
     * 按自增ID更新
     * @param t
     * @return
     */
    @ScriptEvent(script = sScript.class,beForeMethodName = "onUpdate(t)",afterMethodName = "onUpdated(result)")
    @Transactional(rollbackFor = DbException.class)
    @ThreadLock(patten = ThreadLockPattern.OnSingleThread)
    @Override
    public T updateById(T t) {
        AnnotationHelper.handleModelFiled(t);
        if(sDevFastConfig.getVerifyLevel() >= 2) {
            CommonVerifyModel(t);
        }
        //实际更新数据
        if(!retBool(baseMapper.updateById(t))) {
            throw new DbException("更新数据失败");
        }
        //缓存更新
        RedisCacheHelper.add(t,this.getClass(),true);
        this.onUpdate(t);
        this.onChange();
        return t;
    }

    /**
     * 按条件删除
     * @param wrapper
     */
    @ScriptEvent(script = sScript.class,beForeMethodName = "onDestroy(wrapper)",afterMethodName = "onDestroyed(result)")
    @Transactional(rollbackFor = DbException.class)
    @Override
    public List<T> delete(QueryWrapper<T> wrapper)
    {
        //删之前先查出来,用来给脚本回调
        List<T> result=query(wrapper);
        SqlHelper.delBool(baseMapper.delete(wrapper));
        //删除缓存
        RedisCacheHelper.delete((List<Object>) result,this.getClass());
        this.onDelete(result);
        this.onChange();
        return result;
    }

    /**
     * 按条件获取一条数据
     * @param wrapper
     * @return
     */
    @Override
    public T getOne(QueryWrapper<T> wrapper)
    {
        T result = SqlHelper.getObject(baseMapper.selectList(wrapper));
        if(sDevFastConfig.getVerifyLevel() >= 3) {
            CommonVerifyModel(result);
        }
        //压入缓存
        RedisCacheHelper.add(result,this.getClass(),false);
        return result;
    }

    /**
     * 按条件获取批量数据
     * @param wrapper
     * @return
     */
    @Override
    public List<T> query(QueryWrapper<T> wrapper)
    {
        if(null == wrapper)
            wrapper=new QueryWrapper<>();
        SortType sortType=AnnotationHelper.getModelGmtSortType(currentModelClass());
        if(null != sortType && sortType !=SortType.NONE) {
            //按指定的排序方式排序
            String GmtCreateName = AnnotationHelper.getAnnotationFieldName(GmtCreate.class, currentModelClass());
            if (sortType == SortType.AESC)
                wrapper.orderByAsc(GmtCreateName);
            else
                wrapper.orderByDesc(GmtCreateName);
        }

        List<T>  result=baseMapper.selectList(wrapper);

        if(sDevFastConfig.getVerifyLevel() >= 3)
        {
            CommonVerifyModel(result);
        }
        //压入缓存
        RedisCacheHelper.add((List<Object>)result,this.getClass(),false);
        return result;
    }

    /**
     * 分页查询
     * @param wrapper
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public IPage<T> pageQuery(QueryWrapper<T> wrapper, int pageNo, int pageSize)
    {
        if(pageNo <= 0 || pageSize<=0)
            throw new DataException("分页查询pageNo不能小于1,pageSize不能小于等于0");
        IPage<T> page=new Page<>(pageNo,pageSize);
        if(null == wrapper) {
            return baseMapper.selectPage(page,null);
        }

        SortType sortType=AnnotationHelper.getModelGmtSortType(currentModelClass());
        if(null != sortType && sortType !=SortType.NONE) {
            //按指定的排序方式排序
            String GmtCreateName = AnnotationHelper.getAnnotationFieldName(GmtCreate.class, currentModelClass());
            if (sortType == SortType.AESC)
                wrapper.orderByAsc(GmtCreateName);
            else
                wrapper.orderByDesc(GmtCreateName);
        }
        //确保record不能为null
        IPage<T> result=baseMapper.selectPage(page,wrapper);
        if(null==result.getRecords())
            result.setRecords(new ArrayList<T>());
        //验证级别大于等于3则执行验证
        if(sDevFastConfig.getVerifyLevel() >= 3) {
           CommonVerifyModel(result.getRecords());
        }
        //压入缓存
        RedisCacheHelper.add(result,this.getClass(),false);
        return result;
    }

    /**
     * 按条件统计数据条数
     * @param wrapper
     * @return
     */
    @Override
    public int count(QueryWrapper<T> wrapper) {
        return SqlHelper.retCount(baseMapper.selectCount(wrapper));
    }

    /**
     * 获取表的数据总条数
     * @return
     */
    @Override
    public int count()
    {
        return count(null);
    }


    @Override
    public Collection<T> queryByIds(Collection<? extends Serializable> idList) {
        Collection<T> result= baseMapper.selectBatchIds(idList);
        if(sDevFastConfig.getVerifyLevel() >= 3) {
            CommonVerifyModel((T) result.toArray());
        }
        return result;
    }

    @Override
    public void onInit()
    {
        CacheModel cacheModel= ClassHelper.getClassCacheModel(this.getClass());
        if(null == cacheModel)
            return;
        if(cacheModel == CacheModel.ALL && count()>10000)
            throw new DataException(currentModelClass().getSimpleName()+"表的数据大于1万条,请将缓存模式改为@OnRedisCache(model = CacheModel.DUNAMIC)");
        //如果是全部在缓存模式就讲表全部加载进来
        if(cacheModel == CacheModel.ALL)
        {
            log.info("加载"+currentModelClass().getSimpleName()+"表的数据到缓存中");
            query(new QueryWrapper<>());
            log.info(currentModelClass().getSimpleName()+"表的数据加载完毕");
        }
    }
}
